#!/usr/bin/env python
import sys
import logging

from twisted.internet import reactor

import yadtshell


logger = logging.getLogger('yadtshell')
reactor.return_code = 127

cmd = None
if len(sys.argv) > 1:
    cmd = sys.argv[1]

from optparse import OptionParser, OptionGroup
parser = OptionParser(usage='%prog [options] {status|info|dump|start|stop|ignore|unignore|lock|unlock} [uri ...]')

# global stuff
parser.add_option('-n', '--dryrun', action='store_true', dest='dryrun', default=False, help='does nothing')
parser.add_option('-v', '', action='store_true', dest='verbose', default=False, help='verbose output')

# status stuff
if cmd in ['status']:
    parser.add_option('', '--use-cache-only', action='store_true', default=False, dest='use_cache_only', help='dont query hosts, but use cached data only')

# parallel actions stuff
if cmd in ['start', 'stop', 'ignore', 'unignore', 'lock', 'unlock', 'update', 'updateartefact']:
    parser.add_option('-p', '--parallel', action='store', dest='parallel', default='1', help='parallel execution, where and how')

# message for lock, ignore
if cmd in ['ignore', 'lock']:
    group = OptionGroup(parser, '(MANDATORY) reason for command')
    group.add_option('-m', '--message', dest='message')
    group.add_option('', '--force', action='store_true', dest='force', default=False, help='force the command')
    parser.add_option_group(group)

# dump stuff
if cmd in ['dump']:
    group = OptionGroup(parser, 'dumping information')
    group.add_option('', '--attribute', action='store', dest='attribute', help='queries given attribute only')
    group.add_option('', '--show-pending-updates', action='store_const', dest='filter', const='pending-updates', help='show all pending updates for target')
    group.add_option('', '--show-current-artefacts', action='store_const', dest='filter', const='current-artefacts', help='show all artefacts on target')
    parser.add_option_group(group)

# info stuff
if cmd in ['info']:
    parser.add_option('', '--full', action='store_true', dest='full', default=False, help='shows all artefacts')

if not cmd:
    parser.print_help()
    sys.exit(1)


opts, args = parser.parse_args()

args = args[1:]

if opts.verbose:
    yadtshell.settings.ch.setLevel(logging.DEBUG)

component_names = args
if component_names:
    components = yadtshell.util.restore_current_state()
    component_names = yadtshell.helper.expand_hosts(component_names)
    component_names = yadtshell.helper.glob_hosts(components, component_names)

def create_simple_plan(cmd, component_names):
    action_set = set()
    for component_name in component_names:
        component = components.get(component_name, None)
        if not component:
            component = components[yadtshell.uri.change_version(component_name, 'current')]
        if not component:
            logger.warning('could not resolve uri %s' % component_name)
            continue
        action_set.add(yadtshell.actions.Action(cmd, component.uri, kwargs=vars(opts)))
    plan = yadtshell.actions.ActionPlan(cmd, action_set)
    yadtshell.util.dump_action_plan(cmd, plan)
    return plan

def createDeferredFromPlan(plan):
    plan = yadtshell.metalogic.apply_instructions(plan, opts.parallel)
    yadtshell.util.dump_action_plan(cmd, plan)
    am = yadtshell.ActionManager()
    return am.action(flavor=cmd, **vars(opts))



deferred = None
if cmd == 'status':
    deferred = yadtshell.status(hosts=args, **vars(opts))
elif cmd == 'info':
    yadtshell.info(**vars(opts))
    sys.exit(0)
elif cmd == 'dump':
    yadtshell.dump(args, **vars(opts))
    sys.exit(0)
elif cmd == 'update':
    deferred = yadtshell.status()
    deferred.addCallback(
        yadtshell.update.compare_versions, 
        args,
        **vars(opts)
    )
    am = yadtshell.ActionManager()
    deferred.addCallback(am.action, **vars(opts))
elif cmd in ['ignore', 'unignore', 'lock', 'unlock', 'updateartefact']:
    plan = create_simple_plan(cmd, component_names)
    deferred = createDeferredFromPlan(plan)
else:
    try:
        plan = yadtshell.metalogic.metalogic(cmd, component_names)
        deferred = createDeferredFromPlan(plan)
    except Exception, e:
        logger.critical('an error occured while trying to "%s %s"' % (cmd, ', '.join(args)))
        logger.debug(e)
        sys.exit(1)


try:
    deferred.addErrback(yadtshell.twisted.report_error, logger.critical, include_stacktrace=False)
    deferred.addBoth(yadtshell.twisted.stop_and_return)
    deferred.addErrback(yadtshell.twisted.report_error, logger.critical)
    if not reactor.running:
        reactor.run()
except yadtshell.actions.ActionException, e:
    reactor.return_code = e.exitcode
    msg = e.message
    if e.rootcause:
        msg += ': ' + str(e.rootcause)
    logger.critical(msg)

sys.exit(reactor.return_code)

